/***************************************************************************
 *   mgpi_thrlib.c - Multi-Threading Library Definitions                   *
 *   Copyright (C) 2008 by cmaster.matso                                   *
 *   cmaster.matso@gmail.com                                               *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/

#include "mgpi_thrmdl.h"

static int s_nThreadsNumber = 1;                                                /** threads counter */

static struct {
    int o, h;
} *s_pThreadsIDs = NULL;                                                        /** ??? */

static int s_nThreadsIDsLength = 0;                                             /** ??? */
static SDL_Thread *s_aAsyncCallsThreads[MGPI_MAX_ASYNC_CALLS];                  /** SDL threads for asynchronous calls */
static int s_nFree = 0;

/**
 * Adds a thread to thread tree.
 */
int MGPI_AddThread(tree_t **tree, int (*thrp)(void *), void *vparam) {
    tree_t *temp;

    if (!s_pThreadsIDs) {
        s_pThreadsIDs = calloc(1, sizeof *s_pThreadsIDs);
        s_nThreadsIDsLength = 1;
    } else {
        s_nThreadsIDsLength++;
        s_pThreadsIDs = realloc(s_pThreadsIDs, s_nThreadsIDsLength * sizeof *s_pThreadsIDs);
    }

    if (!(*tree)) { /* for the tree root (main thread) */
        (*tree) = (tree_t *) calloc(1, sizeof (tree_t));
        (*tree)->parent = NULL;
        (*tree)->children = NULL;
        (*tree)->nchld = 0;
        (*tree)->data = (thrctrl_t *) calloc(1, sizeof (thrctrl_t));
        ((thrctrl_t *) (*tree)->data)->hight = 0;
        ((thrctrl_t *) (*tree)->data)->offset = 0;
        ((thrctrl_t *) (*tree)->data)->thread = NULL;
    }
    /* for any other tree nodes (subthreads) */
    temp = (tree_t *) calloc(1, sizeof (tree_t));
    temp->parent = (*tree);
    temp->nchld = 0;
    temp->data = (thrctrl_t *) calloc(1, sizeof (thrctrl_t));
    s_pThreadsIDs[s_nThreadsIDsLength - 1].h =
            ((thrctrl_t *) temp->data)->hight = ((thrctrl_t *) (*tree)->data)->hight + 1;
    s_pThreadsIDs[s_nThreadsIDsLength - 1].o =
            ((thrctrl_t *) temp->data)->offset = (*tree)->nchld;

    if ((*tree)->children) {
        (*tree)->children = (tree_t **) realloc((*tree)->children, ((*tree)->nchld + 1) * sizeof (tree_t));
        (*tree)->children[(*tree)->nchld] = temp;
        ++(*tree)->nchld;
    } else {
        (*tree)->children = (tree_t **) calloc(1, sizeof (tree_t *));
        (*tree)->children[0] = temp;
        ++(*tree)->nchld;
    }
    ((thrctrl_t *) temp->data)->vparam = vparam;
    ((thrctrl_t *) temp->data)->thread = (SDL_Thread *) SDL_CreateThread(thrp, temp);
    s_nThreadsNumber++;

    return (s_nThreadsIDsLength - 1);
}

/**
 * Gets a thread of the given ID form the thread tree.
 */
SDL_Thread *MGPI_GetThread(tree_t *tree, int thr) {
    SDL_Thread *t = NULL;
    int i = tree->nchld - 1;

    if (tree->nchld && tree->children) while (i >= 0 && !t) t = MGPI_GetThread(tree->children[i--], thr);

    if (!t) {
        if (((thrctrl_t *) tree->data)->offset == s_pThreadsIDs[thr].o &&
                ((thrctrl_t *) tree->data)->hight == s_pThreadsIDs[thr].h) t = ((thrctrl_t *) tree->data)->thread;
        else t = NULL;
    }
    return t;
}

/**
 * Returns number of threads in the thread tree.
 */
int MGPI_GetThreadsNumber(void) {
    return ((!s_nThreadsNumber) ? (s_nThreadsNumber = 1) : (s_nThreadsNumber));
}

/**
 * Deletes threads form the thread tree.
 */
int MGPI_DeleteThreads(tree_t *tree) {
    int i = tree->nchld - 1;

    if (s_pThreadsIDs) {
        free(s_pThreadsIDs);
        s_nThreadsIDsLength = 0;
        s_pThreadsIDs = NULL;
    }

    if (tree->nchld && tree->children)
        while (i >= 0) {
            MGPI_DeleteThreads(tree->children[i--]);
            --tree->nchld;
        }

    if (tree->parent) tree->parent->children[((thrctrl_t *) tree->data)->offset] = NULL;
    tree->parent = NULL;
    free(tree->data);
    free(tree);
    s_nThreadsNumber--;

    return (!0);
}

/**
 * An asynchronous call.
 */
void MGPI_AsyncCall(int (*asyncCall)(void *), void *ptr) {
    s_aAsyncCallsThreads[s_nFree++] = SDL_CreateThread(asyncCall, ptr);
    return;
}

/**
 * Synchronizes asynchronous calls.
 */
void MGPI_SynchCalls(int *status[MGPI_MAX_ASYNC_CALLS]) {
    int i = 0;

    while (i < s_nFree) SDL_WaitThread(s_aAsyncCallsThreads[i], status[i]);
    s_nFree = 0;

    return;
}
